package cn.maihe.elg.operation.supports.bank.cmb.tradingmanager;

import cn.maihe.elg.operation.supports.bank.cmb.tradingmanager.base.AR;
import cn.maihe.elg.operation.supports.bank.cmb.tradingmanager.pojo.CommonRequestParam;
import cn.maihe.elg.operation.supports.bank.cmb.tradingmanager.pojo.ConfigDTO;
import cn.maihe.elg.operation.supports.bank.cmb.tradingmanager.utils.CommonUtil;
import cn.maihe.elg.operation.supports.bank.cmb.tradingmanager.utils.DCCryptor;
import cn.maihe.elg.operation.supports.bank.cmb.tradingmanager.utils.DCHelper;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.Security;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 招商银行工具类
 *
 * @version V1.0
 * @author: Sidon
 * @createTime: 2022-04-21 14:30
 * Copyright (C)2021-2022 福建省金柠网络科技有限公司. All rights reserved.
 */
@Data
@Slf4j
public class ServiceSupport {

    private static Base64.Encoder encoder = Base64.getEncoder();
    private static Base64.Decoder decoder = Base64.getDecoder();


    public String publicKey;
    public String privateKey;
    public String sm4Key;
    public String URL;
    public String UID;

    public ServiceSupport(ConfigDTO configDTO) {
        this.publicKey = configDTO.getPublicKey();
        this.privateKey = configDTO.getPrivateKey();
        this.sm4Key = configDTO.getSm4Key();
        this.UID = configDTO.getUserId();
        this.URL = configDTO.getUrl();
    }

    static {
        // 引入BC库
        Security.addProvider(new BouncyCastleProvider());
    }

    public static CommonRequestParam.Head getHeaders(String funcode, String reqid, String userid) {
        return CommonRequestParam.Head.builder().funcode(funcode).reqid(reqid).userid(userid).build();
    }

    public AR<Object> doProcess(JsonObject jObject) {

        try {
            JsonObject object = new JsonObject();
            // 签名
            object.addProperty("sigdat", "__signature_sigdat__");
            object.addProperty("sigtim", DCHelper.getTime());

            jObject.add("signature", object);
            String source = DCHelper.serialJsonOrdered(jObject);
            log.info("[招商]请求参数(签名原文):{}", source);

            byte[] signature1 = DCCryptor.CMBSM2SignWithSM3(getID_IV(), decoder.decode(privateKey), source.getBytes(StandardCharsets.UTF_8));
            String sigdat1 = new String(encoder.encode(signature1));
            //log.info("签名结果: {}",sigdat1);
            object.addProperty("sigdat", sigdat1);


            // SM4-CBC加密
            String plaintxt = jObject.toString();
            //log.info("加密前req:  {}",plaintxt);
            byte[] enInput = DCCryptor.CMBSM4EncryptWithCBC(sm4Key.getBytes(), getID_IV(), plaintxt.getBytes(StandardCharsets.UTF_8));
            String req = new String(encoder.encode(enInput));
            //log.info("加密后req:  ",req);


            // 发送请求
            HashMap<String, String> map = new HashMap<>();


            String fundCode = null;
            String reg = "(?<=funcode\":\")\\w+";
            Pattern pattern = Pattern.compile(reg);
            Matcher matcher = pattern.matcher(source);
            if (matcher.find()) {
                fundCode = matcher.group();
                if (CommonUtil.isEmpty(fundCode)) {
                    return AR.fail("获取FUNCODE失败");
                }
            }

            map.put("UID", UID);
            map.put("FUNCODE", fundCode);
            map.put("ALG", "SM");
            map.put("DATA", URLEncoder.encode(req, "UTF-8"));

            // 发送请求
            log.info("交易管家 请求地址：{}请求参数:{}",URL,map);
            String res = DCHelper.doPostForm(URL, map);
            log.info("交易管家 响应结果:{}",res);

            if(res.contains("ErrMsg")){
                return AR.fail(res);
            }

            // 解密请求
            String resplain = new String(DCCryptor.CMBSM4DecryptWithCBC(sm4Key.getBytes(), getID_IV(), decoder.decode(res)), StandardCharsets.UTF_8);
            //System.out.println("res decrypt: " + resplain);

            JsonObject object2;
            try {
                object2 = new GsonBuilder().create().fromJson(resplain, JsonObject.class);
            } catch (Exception e) {
                e.printStackTrace();
                return AR.fail(resplain);
            }
            // 验签
            JsonObject object3 = object2.getAsJsonObject("signature");
            String resSign = object3.get("sigdat").getAsString();
            object3.addProperty("sigdat", "__signature_sigdat__");
            object2.add("signature", object3);

            String resSignSource = DCHelper.serialJsonOrdered(object2);
            log.info("[招商]响应参数(验签原文): {}", resSignSource);
//        log.info("验签签名值: " + resSign);
            boolean verify = DCCryptor.CMBSM2VerifyWithSM3(getID_IV(), decoder.decode(publicKey), resSignSource.getBytes(StandardCharsets.UTF_8), decoder.decode(resSign));
            log.info("[招商]响应参数(验签结果): {}", verify);
            if (verify) {
                final JsonObject headObject = object2.getAsJsonObject("response").getAsJsonObject("head");
                if ("SUC0000".equals(headObject.get("resultcode").getAsString())) {
                    return AR.success(object2.getAsJsonObject("response").get("body").toString());
                } else {
                    return AR.fail(headObject.get("resultmsg").toString());
                }
            }
            return AR.fail();
        } catch (Exception e) {
            log.error("[招商]云直连执行出现异常：{}", e.getMessage());
            e.printStackTrace();
        }
        return AR.fail("[招商]云直连执行出现异常");
    }


    private byte[] getID_IV() {
        String userid = UID + "0000000000000000";
        return userid.substring(0, 16).getBytes();
    }

    public static String getMillisTime() {
        Date date = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        return dateFormat.format(date);
    }
}